#include "wordpredictor.h"
#include "datakeyboard.h"
#include <cmath>
#include <algorithm>
#include <QDebug>

//#define DEBUG_LOG

const char charTable[27] = "qwertyuiopasdfghjklzxcvbnm";
const int charIndex[26] = {10, 23, 21, 12, 2, 13, 14, 15, 7, 16, 17, 18, 25, 24, 8, 9, 0, 3, 11, 4, 6, 22, 1, 20, 5, 19};

WordPredictor::WordPredictor(QObject *parent) : QObject(parent), currentLen(0), curIndex(0)
{
    m_datakeyboard = new DataKeyboard;
    WordPredictor::initConts();
    connect(m_datakeyboard, SIGNAL(keyTapped(float, float)), this, SLOT(receiveOneTap(float, float)) );
#ifdef USE_GESTURE
    connect(m_datakeyboard, SIGNAL(gestureTriggered(int)), this, SLOT(receiveGesture(int)));
#endif
    for(int i = 0; i < 5; ++i)
        candidateWords.push_back("");
    this->loadWordTable();
    m_datakeyboard->start();
}

void WordPredictor::initConts()
{
    Contf deleteCont = {cv::Point2f(224.155, 9.60998), cv::Point2f(210.095, 5.28855), cv::Point2f(185.17, 0.788315), cv::Point2f(169.486, -1.76678), cv::Point2f(158.673, 6.28004),cv::Point2f(156.023, 18.1138),cv::Point2f(156.175, 21.8299) ,
                        cv::Point2f(157.354, 29.7942),cv::Point2f(158.572, 35.2309),cv::Point2f(173.841, 39.1382),cv::Point2f(197.717, 42.773),cv::Point2f( 216.204, 44.2495),cv::Point2f( 238.762, 40.7888),cv::Point2f( 238.699, 36.9833)};
    Contf leftCont = {cv::Point2f(80.8458, 83.3156), cv::Point2f(8.27117, 81.583), cv::Point2f(9.47552, 97.6149), cv::Point2f(76.8946, 99.7665), cv::Point2f(79.473, 96.3377)};
    Contf rightCont = {cv::Point2f(258.872, 95.8623), cv::Point2f(216.798, 93.1154), cv::Point2f( 178.302, 90.6697), cv::Point2f(179.334, 101.602), cv::Point2f(182.925, 107.135), cv::Point2f(253.849, 113.405), cv::Point2f(258.316, 107.864)};
    Contf spaceCont = {cv::Point2f(156.153, 88.0648), cv::Point2f(165.729, 90.1949), cv::Point2f(169.673, 100.848), cv::Point2f(168.84, 115.988), cv::Point2f( 82.4017, 111.756), cv::Point2f(79.0535, 105.311), cv::Point2f(80.6323, 97.4577),
                       cv::Point2f(81.9797, 85.2626), cv::Point2f(85.0761, 84.0987)};
    Contf alphaCont = {cv::Point2f(221.209, 91.5617),cv::Point2f(237.689, 85.05),cv::Point2f(248.241, 79.7648),cv::Point2f(252.107, 70.6873),cv::Point2f( 247.609, 58.8678),cv::Point2f( 245.758, 54.9335), cv::Point2f(243.845, 49.8217),
                        cv::Point2f( 242.265, 47.1547), cv::Point2f(238.966, 45.374), cv::Point2f( 235.906, 43.6348), cv::Point2f(230.829, 43.6234), cv::Point2f( 222.494, 41.5791), cv::Point2f(217.074, 40.6404), cv::Point2f(213.163, 39.8004),
                       cv::Point2f(177.446, 36.9917), cv::Point2f(162.362, 32.6056), cv::Point2f( 151.991, 32.2474), cv::Point2f(68.4925, 25.7276), cv::Point2f(36.7331, 27.9063), cv::Point2f(25.6973, 33.9247), cv::Point2f( 13.1427, 52.2835),
                       cv::Point2f(14.6117, 69.181), cv::Point2f(18.2797, 80.0084), cv::Point2f(24.9125, 82.826), cv::Point2f(52.9793, 88.7788), cv::Point2f(125.115, 94.3647)};
    this->deleteConts.push_back(deleteCont);
    this->leftConts.push_back(leftCont);
    this->rightConts.push_back(rightCont);
    this->spaceConts.push_back(spaceCont);
    this->alphaConts.push_back(alphaCont);
}

bool WordPredictor::isDeleteKey(float x, float y)
{
    //return (x >= 202 && x <= 232 && y >= 1 && y <= 24);
    return cv::pointPolygonTest(WordPredictor::deleteConts[0], cv::Point2f(x, y), false) > 0;
}

bool WordPredictor::isLeftKey(float x, float y)
{
    //return (x >= 0 && x <= 73 && y >= 78 && y <= 88);
    return cv::pointPolygonTest(WordPredictor::leftConts[0], cv::Point2f(x, y), false) > 0;
}

bool WordPredictor::isRightKey(float x, float y)
{
    return cv::pointPolygonTest(WordPredictor::rightConts[0], cv::Point2f(x, y), false) > 0;
    //return (x >= 187 && x <= 248 && y >= 62 && y <= 89)||(x >= 174 && x <= 247 && y >= 78 && y <= 88);
}

bool WordPredictor::isSpaceKey(float x, float y)
{
    return cv::pointPolygonTest(WordPredictor::spaceConts[0], cv::Point2f(x, y), false) > 0;
    //return (x >= 77 && x <= 150 && y >= 79 && y <= 88 ) || (x >= 12 && x <= 197 && y >= 0 && y <= 23);
}

bool WordPredictor::isAlphaKey(float x, float y)
{
    return cv::pointPolygonTest(WordPredictor::alphaConts[0], cv::Point2f(x, y), false) > 0;
}

static float GaussPro(float x, float y, float mu1, float mu2, float delta1, float delta2)
{
    return 1.0 / (2 * M_PI * delta1 * delta2 ) * exp(-1.0f / 2 * ( (x - mu1) * (x - mu1 ) / (delta1 * delta1) + (y - mu2) * (y - mu2) / (delta2 * delta2)));
}

float WordPredictor::getPWFromInput(const char* word, size_t len)
{
    float p = 1;
    for(size_t i = 0; i < len; ++i) {
        p *= GaussPro(inputSequence[i].x, inputSequence[i].y, charPosTable[charIndex[word[i] - 'a']].x, charPosTable[charIndex[word[i] - 'a']].y, 5, 5);
    }
    return p;
}

static bool cmp(const WordNode& x, const WordNode& y) {
    return x.frequency > y.frequency;
}

void WordPredictor::loadWordTable()
{
    FILE* wordTableFp = fopen(Constants::wordtextPath, "r");
    FILE* freTableFp = fopen(Constants::frequencyPath, "r");
    FILE* charPosFp = fopen(Constants::centerPosesPath, "r");
    char wordBuffer[50] = {0};
    float tempFrequency;
    if(!(wordTableFp && freTableFp && charPosFp))
        throw "Cannot load word table";
    else {
        while(fgets(wordBuffer, 50, wordTableFp)) {
            fscanf(freTableFp, "%f", &tempFrequency);
            size_t len = strlen(wordBuffer);
            wordBuffer[len - 1] = '\0';
            srcWords.push_back(WordNode(wordBuffer, len, tempFrequency));
            tempWords.push_back(WordNode(wordBuffer, len, tempFrequency));
            memset(wordBuffer, 0, sizeof(wordBuffer));
        }
        for(int i = 0; i < srcWords.size(); ++i) {
            size_t word_len = strlen(srcWords[i].t);
            rawWords[word_len].push_back(srcWords[i]);
            useWords[word_len].push_back(srcWords[i]);
        }
        for(int i = 0; i < 26; ++i) {
            fscanf(charPosFp, "%f %f", &(this->charPosTable[i].x), &(this->charPosTable[i].y) );
        }
    }
}

size_t min(size_t x, size_t y) {
    return x < y ? x : y;
}

void WordPredictor::updateCandidateWords()
{
    useWords[currentLen].clear();
    for(size_t i = 0; i < rawWords[currentLen].size(); ++i) {
        useWords[currentLen].push_back(WordNode(rawWords[currentLen][i].t, currentLen,
                                                rawWords[currentLen][i].frequency * getPWFromInput(rawWords[currentLen][i].t, currentLen)));
    }
    std::sort(useWords[currentLen].begin(), useWords[currentLen].end(), cmp);
    /*
    qDebug() << " useWords len" << useWords[currentLen].size();
    for(int i = 0; i < 5; ++i)
        qDebug() << useWords[currentLen][i].t << " ";
    */
    for(int i = 5 - useWords[currentLen].size(); i > 0; --i) {
        candidateWords[5 - i] = "";
    }
    for(int i = 0; i < min(5, useWords[currentLen].size()); ++i) {
        candidateWords[i] = std::string(useWords[currentLen][i].t, currentLen);
    }
    /*
    qDebug() << "Sequence is ";
    for(int i = 0; i < inputSequence.size(); ++i) {
        qDebug() << inputSequence[i].x << " " << inputSequence[i].y << " ";
    }
    */
    /*
    qDebug() << "Words ";
    for(int i = 0; i < 5; ++i)
        qDebug() << candidateWords[i].c_str() << " ";
    */
    emit candidateWordsChanged();
    updateHintWord(0);
}

void WordPredictor::updateHintWord(int index)
{
    curIndex = index;
    emit hintWordChanged(index);
}

void WordPredictor::receiveGesture(int index)
{
#ifdef DEBUG_LOG
    qDebug() << "gesture " << index;
#endif
    if(index == 1 && currentLen > 0)
        clearInput();
    else if(index == 2 && currentLen > 0) {
        deleteOneChar();
    }
    else if(index == 3 && currentLen > 0) {
        moveCursorLeft();
    }
    else if(index == 4 && currentLen > 0)
        moveCursorRight();
}

void WordPredictor::moveCursorLeft()
{
    if(curIndex > 0)
        updateHintWord(curIndex - 1);
    else updateHintWord(candidateWords.size() - 1);
}

void WordPredictor::moveCursorRight()
{
    if(curIndex < 4)
        updateHintWord(curIndex + 1);
    else updateHintWord(0);
}

void WordPredictor::deleteOneChar()
{
    if(currentLen > 0) {
        currentLen--;
        inputSequence.pop_back();
        updateCandidateWords();
    }
}



void WordPredictor::spaceButtonClicked()
{
    if(currentLen > 0)
        clearInput();
}



void WordPredictor::receiveOneTap(float x, float y)
{
    //qDebug() << "pos " << x << " " << y;

    #ifndef USE_GESTURE
#ifndef FULL_BAYES
    if(currentLen > 0 && isSpaceKey(x, y)) {
        clearInput();
    }
    else if(currentLen > 0 && isDeleteKey(x, y)) {
        deleteOneChar();
    }
    else if(currentLen > 0 && isLeftKey(x, y)) {
        moveCursorLeft();
    }
    else if(currentLen > 0 && isRightKey(x, y)) {
        moveCursorRight();
    }
    else
#endif
    #endif
    if(currentLen < MAX_WORD_LEN
        #ifndef FULL_BAYES
            && isAlphaKey(x, y)
        #endif
            ) {
            inputSequence.push_back(TapPos(x, y));
            currentLen++;
            updateCandidateWords();
    }

}

void WordPredictor::clearInput()
{
    lastWord = candidateWords[curIndex];
#ifdef DEBUG_LOG
    qDebug() << "word " << candidateWords[curIndex].c_str();
#endif

    emit oneWordInputed();
    while(!inputSequence.empty())
        inputSequence.pop_back();
    currentLen = 0;
    updateCandidateWords();
}
